#include "log_macro.hpp"
#include "channel.hpp"
#include "epoller.hpp"
#include <functional>
#include <vector>
#include <thread>
#include <mutex>
#include <memory>
#include <sys/eventfd.h>
#include <unistd.h>

using namespace std;

class event_loop {
using functor = function<void()>;
private:
    static int get_event_fd() {
        int fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
        if (fd == -1) {
            ERROR_LOG("Failed to get event fd");
            exit(errno);
        }
        return fd;
    }

    void read_efd() {
        uint64_t ret = 0;
        ssize_t n = read(_event_fd, &ret, sizeof(ret));
        if (n != sizeof(ret)) {
            if(n == EINTR || n == EAGAIN) {
                return;
            }
            ERROR_LOG("Failed to read event fd");
            exit(errno);
        }
    }
    
    // 防止epoller因为没有事件就绪而阻塞
    void write_efd() {
        uint64_t val = 1;
        ssize_t n = write(_event_fd, &val, sizeof(val));
        if (n != sizeof(val)) {
            if(n == EINTR || n == EAGAIN) {
                return;
            }
            ERROR_LOG("Failed to read event fd");
            exit(errno);
        }
    }

    void run_tasks() {
        vector<functor> _funcs;
        {
            unique_lock<mutex> lock(_mtx);
            _tasks.swap(_funcs);
        }
        for (const auto& func : _funcs) {
            func();
        }
    }

public:
    event_loop()
        : _id(this_thread::get_id()), 
        _event_fd(get_event_fd()), 
        _efd_ch(make_unique<channel>(this, _event_fd)),
        _epoller(make_unique<epoller>())  
    {
        _efd_ch->set_read_callback(bind(&event_loop::read_efd, this));
        _efd_ch->enable_read();
    }

    void start() {
        vector<channel *> readys = _epoller->poller();
        for (const auto& ch : readys) {
            ch->handle_event();
        }
        run_tasks();
    }

    bool is_in_loop() const {
        return _id == this_thread::get_id();
    }

    void run_in_loop(const functor& cb) {
        if (is_in_loop()) {
            cb();
            return;
        } 
        add_to_tasks(cb);
    }

    void add_to_tasks(const functor& cb) {
        {
            unique_lock<mutex> lock(_mtx);
            _tasks.push_back(cb);
        }
        write_efd();
    }

    void update(channel *ch) {
        _epoller->update(ch);
    }

    void remove(channel *ch) {
        _epoller->remove(ch);
    }

private:
    thread::id _id;
    int _event_fd;
    unique_ptr<channel> _efd_ch;
    unique_ptr<epoller> _epoller;
    mutex _mtx;
    vector<functor> _tasks;
};